<template>
  <div class="xr-editor">
			<!--按钮区-->
			<div class="nav">
				<button @click="execCommand('bold')">加粗</button>
				<button @click="execCommand('insertUnorderedList')">无序列表</button>
				<button @click="execCommand('insertHorizontalRule')">水平线</button>
				<button @click="execCommand('undo')">后退</button>
				<button @click="execCommand('redo')">前进</button>
				<button @click="execCommand('formatBlock','<p>')">段落</button>
        <button @click="createLink">链接</button>
        <button @click="insertImgLink">图片链接</button>
        <button class="nav__img">
          插入图片
          <!-- 这个input是隐藏的 -->
          <input type="file" accept="image/gif,image/jpeg,image/png" @change='insertImg'>
        </button>
			</div>
			<div class="row">				
				<!--contenteditable，true为可编辑,false为不可编辑,属性最早是在 IE上实现的,且可以作用于其它标签，不限于 div-->
				<div class="editor" contenteditable='true' @input="print"></div>
				<div class="content">{{html}}</div>
			</div>
			<!--编辑区-->
			
		</div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      html:''
    }
  },
  mounted(){
    this.editor=document.querySelector('.editor');
    this.editor.addEventListener('click',this.handleClick);
  },
  methods: {
    // document.execCommand(命令名称，是否展示用户界面，命令需要的额外参数)
		//document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)
		//第一个参数为一些命令名称;
		//第二个参数直接写false,前期为兼容,现在无影响;
		//第三个参数为一些命令需要额外的参数,如插入图片传url/base64的参数,没有则传null		
		//document.execCommand('bold', false, null);// 加粗
		//document.execCommand('insertImage', false, url || base64);// 添加图片
		//document.execCommand('formatblock', false, '<p>');// 把一段文字用 p 标签包裹起来
    execCommand(name, args = null) {
      // 执行原生的document.execCommand方法，浏览器自身会对contenteditable可编辑区维护一个undo栈和一个redo栈，所以才能执行前进和后退操作
        document.execCommand(name, false, args);
    },
    print() {
      //Selection对象,用来表示用户选择的范围或光标位置（光标可以看作范围重合的特殊状态），一个页面用户可能选择多个范围（如Firefox）。
      // 即Selection包含一个或多个Range对象（Selection可以说成是Range的集合）。
      // 一般情况下，富文本编辑器只有一个选择区域，就是一个Range对象。
      // 常用方法:addRange、removeAllRanges、 collapse 和 collapseToEnd 等等
      //letrange=window.getSelection().getRangeAt(0)//获取选中的内容信息(getRangeAt接受一个索引值,因为会有多个range,此处只有一个,所以写0)
      this.html = document.querySelector('.editor').innerHTML;
    },
    createLink() {
      let url = window.prompt('请输入链接地址');
      if(url) this.execCommand('createLink', url);
    },
    insertImgLink() {
      let imgurl = window.prompt('请输入图片地址');
      if(imgurl)  this.execCommand('insertImage', imgurl);
    },
    insertImg(e){
      let reader=new FileReader();
      let file=e.target.files[0];
      reader.onload=()=>{
        let base64Img=reader.result;
        this.execCommand('insertImage',base64Img);
        document.querySelector('.nav__img input').value='';// 解决同一张图片上传无效的问题
      };
      reader.readAsDataURL(file);//读取图片
    },
    handleClick(e){
      if(e.target&&e.target.tagName&&e.target.tagName.toUpperCase()=='IMG'){
        this.handleClickImg(e.target);
      }
    },
    handleClickImg(img){
      this.nowImg=img;
      this.showOverlay();
    },
    showOverlay(){
      //添加蒙层
      this.overlay=document.createElement('div');
      this.editor.appendChild(this.overlay);
      //定位蒙层和大小
      this.repositionOverlay();
    },
    repositionOverlay(){
      let imgRect=this.nowImg.getBoundingClientRect();
      let editorRect=this.editor.getBoundingClientRect();
      //设置蒙层宽高和位置
      Object.assign(this.overlay.style,{
        position:'absolute',
        top:'${imgRect.top - editorRect.top+this.editor.scrollTop}px',
        left:'${imgRect.left-editorRect.left-1+this.editor.scrollLeft}px',
        width:'${imgRect.width}px',
        height:'${imgRect.height}px',
        boxSizing:'border-box',
        border:'1px dashed red'
      });
      //添加四个顶点拖拽框
      this.createBox();
    },
    createBox(){
      this.boxes=[];
      this.addBox('nwse-resize');//top left
      this.addBox('nwse-resize');//top right
      this.addBox('nwse-resize');//bottom right
      this.addBox('nwse-resize');//bottom left
      this.positionBoxes();//设置四个拖拽框位置
    },
    addBox(cursor) {
      const box = document.createElement('div');
      Object.assign(box.style, {
        position: 'absolute',
        height: '12px',
        width: '12px',
        backgroundColor: 'white',
        border: '1px solid #777',
        boxSizing: 'border-box',
        opacity: '0.80'
      });
      box.style.cursor = cursor;
      box.addEventListener('mousedown', this.handleMousedown);  
      // 顺便添加事件
      this.overlay.appendChild(box);
      this.boxes.push(box);
    },
    positionBoxes() {
      let handleXOffset = `-6px`;
      let handleYOffset = `-6px`;
      [
        { left: handleXOffset, top: handleYOffset },
        { right: handleXOffset, top: handleYOffset },
        { right: handleXOffset, bottom: handleYOffset },
        { left: handleXOffset, bottom: handleYOffset }
      ].forEach((pos, idx) => {
        Object.assign(this.boxes[idx].style, pos);
      });
    },
    handleMousedown(e) {
      this.dragBox = e.target;
      this.dragStartX = e.clientX;
      this.preDragWidth = this.nowImg.width;
      this.setCursor(this.dragBox.style.cursor);
      document.addEventListener('mousemove', this.handleDrag);
      document.addEventListener('mouseup', this.handleMouseup);
    },
    handleDrag(e) {
      // 计算水平拖动距离
      const deltaX = e.clientX - this.dragStartX;
      // 修改图片大小
      if(this.dragBox === this.boxes[0] || this.dragBox ===     this.boxes[3]) { 
        // 左边的两个框
        this.nowImg.width = Math.round(this.preDragWidth - deltaX);
      }else{ 
        // 右边的两个框
        this.nowImg.width = Math.round(this.preDragWidth + deltaX);
      }
      // 同时修改蒙层大小
      this.repositionOverlay();
    },
    handleMouseup() {
      this.setCursor('');
      // 拖拽结束移除事件监听
      document.removeEventListener('mousemove', this.handleDrag);
      document.removeEventListener('mouseup', this.handleMouseup);
    },
    setCursor(value) {
      // 设置鼠标样式
      [document.body, this.nowImg].forEach(el => {
        el.style.cursor = value;
      });
    },
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
/* style里如加上scope，样式对编辑区的内容不生效，因为编辑区里面的内容是后来创建的元素，故要么删除scope，要么用/deep/解决（Vue是这样的） */
	.xr-editor {
				margin: 50px auto;
				width: 1000px;
			}
			
			.xr-editor .nav {
				display: flex;
			}
			
			.xr-editor .nav button {
				cursor: pointer;
			}
			
			.xr-editor .nav__img {
				position: relative;
			}
			
			.xr-editor .nav__img input {
				width: 100%;
				height: 100%;
				position: absolute;
				left: 0;
				right: 0;
				opacity: 0;
			}
			
			.row {
				display: flex;
				width: 100%;
				height: 300px;
			}
			
			.editor {
				flex: 1;
				position: relative;
				margin-left: 20px;
				padding: 10px;
				outline: none;
				border: 1px solid #000;
				overflow-y: scroll;
			}
			
			.editor img {
				max-width: 300px;
				max-height: 300px;
				vertical-align: middle;
			}
			
			.content {
				flex: 1;
				border: 1px solid #000;
				word-break: break-all;
				word-wrap: break-word;
				overflow: scroll;
			}
</style>
